Load required libraries

library("hdxstats")
library("dplyr")
library("ggplot2")
library("RColorBrewer")
library("tidyr")
library("pheatmap")
library("scales")
library("viridis")
library("patchwork")
library("Biostrings")
library("xfun")
library("tidyverse")
source("R/test_script_app2.R")

Parsing Raw Data

Parse input raw data and output QFeatures object instance given a CSV file path for different test cases.

# First test case
csv_filepath <- "/homes/sanjuan/R/x86_64-pc-linux-gnu-library/4.1/hdxstats/extdata/MBP.csv"

# CASE 1: Parse data, given input data file path + parameter file
hdx_data <- extract_hdx_data(csv_filepath, parameter_file = "vignettes/data/myparameters.hdxp")

# CASE 2: Parse data, given input data file path + list of parameters
data <- read_csv(csv_filepath)
myparameters <- make_parameter_file(data)
hdx_data <- extract_hdx_data(csv_filepath, parameters = myparameters)

# CASE 3: Parse data, given input data file path. Work out parameters for pre-processing using interactive mode
hdx_data <- extract_hdx_data(csv_filepath, parameter_file = "vignettes/data/myparameters.hdxp", interactive = TRUE)

Data Analysis of Deuterium Uptake Kinetics

TEST 1:

Perform Data Analysis: Plain fitting

  • Deuterium-update fitting for all peptides
  • Plain fitting - not comparing against a reference state or condition
# INPUT
data_selection <- hdx_data[,1:24]
all_peptides <- rownames(data_selection)[[1]]
starting_parameters <- list(a = NULL, b = 0.001,  d = NULL, p = 1)
# OUTPUT
results <- analyse_kinetics(data = data_selection, 
                            method = "fit", 
                            peptide_selection = all_peptides, 
                            start = starting_parameters)
[1] "INFO: Performing fitting of Deuterium uptake kinetics. Method: 'hdxstats::fitUptakeKinetics' "
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "Could not fit model, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "Could not fit model, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
Error in nlsModel(formula, mf, start, wts) : 
  singular gradient matrix at initial parameter estimates
[1] "model fit failed, likely exessive missing values"
results$method
[1] "fitUptakeKinetics"

Visualise Output from Data Analysis

View fitting curves of Deu-uptake kinetics for all available conditions associated to selected peptides

graphics_kinetic <- visualise_hdx_data(results, type="kinetics") 
[1] "INFO: I found  104  models in your results data."
graphics_kinetic[[1]] | graphics_kinetic[[2]] | graphics_kinetic[[3]]

View forest plots showing the difference between uptake measurements between conditions, plus the dispersion between their respective fitting model parameters

graphics <- visualise_hdx_data(results, type="forest") 
[1] "INFO: I found  104  models in your results data."

Combine graphical outputs in a single canvas

graphics[[1]] | graphics_kinetic[[1]]

Display numerical values as a table

graphics[[1]]$data

Perform Data Analysis: Differential fitting

  • Deuterium-update fitting for data selection
  • Differential fitting - with respect to a single reference peptide
# INPUT 
data_selection <- hdx_data[,1:100]
all_peptides <- rownames(data_selection)[[1]] # get all peptides
starting_parameters <- list(a = NULL, b = 0.0001,  d = NULL, p = 1)

# OUTPUT
results <- analyse_kinetics(data = data_selection, 
                            method = "dfit", 
                            peptide_selection = all_peptides[37], 
                            start = starting_parameters)
[1] "INFO: Performing differential fitting of Deuterium uptake kinetics. Method: 'hdxstats::differentialUptakeKinetics' "
[1] "INFO: You did not specify a 'formula' for your fitting model."
[1] "INFO: Fitting will be performed for (default): 'formula <- value ~ a * (1 - exp(-b*(timepoint)^p)) + d' "
graphics <- visualise_hdx_data(results, type="kinetics")
[1] "INFO: I found  7  models in your results data."

View graphical output for reference peptide shown in LHS Top corner.

graphics

TEST CASE 2

Single-domain antibody (sdAb) binding assays to HOIP.

csv_filepath <- "/homes/sanjuan/R/x86_64-pc-linux-gnu-library/4.1/hdxstats/extdata/N64184_1a2_state.csv" 
data <- read_csv(csv_filepath)
Rows: 3600 Columns: 16── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (3): Protein, Sequence, State
dbl (11): Start, End, MaxUptake, MHP, Exposure, Center, Center SD, Uptake, Uptake SD, RT, RT SD
lgl  (2): Modification, Fragment
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
make_parameter_file(data, save = "vignettes/data/N64184_1a2_state.hdxp")
Error in make_parameter_file(data, save = "vignettes/data/N64184_1a2_state.hdxp") : 
  could not find function "make_parameter_file"
hdx_data <- extract_hdx_data(csv_filepath, parameter_file = "vignettes/data/N64184_1a2_state.hdxp")
[1] "INFO: You gave me a CSV file of your HDX-MSM data"
[1] "INFO: I will pre-process your data parse it using QFeatures ..."
[1] "INFO: You provided a 'parameter_file', I will extract parameters from this to format your output QFeatures data object."
[1] "INFO: You provided a list of 'parameters', I will extract parameters from this to format your output QFeatures data object."
[1] "INFO: Stripped your 'Exposure_Time' values from non-numeric characters."
[1] "INFO: Your original_time_units == 'm'. I will convert your 'Exposure_Time' values to seconds (s)."
[1] "INFO: Your 'Replicate' column appears to be NA. I will add this column with 1 values just to label your data."
[1] "INFO: Your 'Charge' column appears to be NA. I will add this column with 0 values just to label your data."
[1] "INFO: Reformatting your data to a wide format..."
[1] "INFO: Parsing your data as a qDF object class instance. Method: parseDeutData"
[1] "WARNING: Your output data is not normalised."
[1] "WARNING: Your output data was not saved. You can provide an output path with 'save = my_path'"
[1] "INFO: I pre-processed you input CSV data content and now it's available as a QFeatures instance"

# INPUT 
data_selection <- hdx_data[,1:33]
all_peptides <- rownames(data_selection)[[1]] # get all peptides
starting_parameters <- list(a = NULL, b = 0.01,  d = NULL)
formula = value ~ a * (1 - exp(-b*(timepoint))) + d

# OUTPUT
results <- analyse_kinetics(data = data_selection, 
                            method = "dfit", 
                            peptide_selection = all_peptides[3], 
                            start = starting_parameters,
                            formula = formula)
[1] "INFO: Performing differential fitting of Deuterium uptake kinetics. Method: 'hdxstats::differentialUptakeKinetics' "
[1] "INFO: You specified your own 'formula' for your fitting model."
graphics <- visualise_hdx_data(results, type="kinetics")
[1] "INFO: I found  11  models in your results data."
custom_colors <- scale_color_manual(values = colorRampPalette(brewer.pal(8, name = "Set2"))(11))
graphics + custom_colors
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

TEST CASE 3

Original raw data

#csv_filepath <- "inst/extdata/Project_2_SecA_Cluster_Data.csv"
#data <- read_csv(csv_filepath)
#data$Replicate <- unlist(lapply(strsplit(data$File, split="_"), function(x) tail(x, n=1)))
#write_csv(data, file = "inst/extdata/Project_2_SecA_Cluster_Data_edited.csv")

csv_filepath <- "inst/extdata/Project_2_SecA_Cluster_Data_edited.csv"

Had to pre-process its content to add Replicate column

data <- read_csv(csv_filepath)
Rows: 31855 Columns: 16── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (4): Protein, Sequence, State, File
dbl (10): Start, End, MaxUptake, MHP, Exposure, z, RT, Inten, Center, Replicate
lgl  (2): Modification, Fragment
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
hdx_data <- extract_hdx_data(csv_filepath, parameter_file = "vignettes/data/Project_2_SecA_Cluster_Data.hdxp")
[1] "INFO: You gave me a CSV file of your HDX-MSM data"
[1] "INFO: I will pre-process your data parse it using QFeatures ..."
[1] "INFO: You provided a 'parameter_file', I will extract parameters from this to format your output QFeatures data object."
[1] "INFO: You provided a list of 'parameters', I will extract parameters from this to format your output QFeatures data object."
[1] "INFO: Stripped your 'Exposure_Time' values from non-numeric characters."
[1] "INFO: Your original_time_units == 'm'. I will convert your 'Exposure_Time' values to seconds (s)."
[1] "INFO: Reformatting your data to a wide format..."
[1] "INFO: Parsing your data as a qDF object class instance. Method: parseDeutData"
Making assay rownames unique.
[1] "WARNING: Your output data is not normalised."
[1] "WARNING: Your output data was not saved. You can provide an output path with 'save = my_path'"
[1] "INFO: I pre-processed you input CSV data content and now it's available as a QFeatures instance"
pheatmap(t(assay(hdx_data)),
         cluster_rows = FALSE, 
         cluster_cols = FALSE,
         color = brewer.pal(n = 9, name = "BuPu"),
         main = "secA heatmap", 
         fontsize = 14,
         legend_breaks = c(0, 1, 2, 3, 4, 5, 6, max(assay(hdx_data))),
         legend_labels = c("0", "1", "2", "3", "4", "5", "6", "Incorporation"))

graphics <- visualise_hdx_data(results, type="kinetics") # READY
graphics <- visualise_hdx_data(results, type="forest") # READY

# NOTE: This only works to compare ONLY TWO distinct conditions, for the same timepoint, for all peptide fragments
# This needs to define a BASELINE state/condition and an ALTERNATE sate/condition
# NOTE: Data must be normalised by intercept 
graphics <- visualise_hdx_data(results, type="manhatten", reference = NULL)# <<<<--- NEXT

# NOTE: NOT SURE ??
graphics <- visualise_hdx_data(results, type="epitope", level="peptide", fasta = "my_fasta_path") # Return an EpitopeMap
graphics <- visualise_hdx_data(results, type="epitope", level="residue", fasta = "my_fasta_path") # Return heatmap
graphics <- visualise_hdx_data(results, type="epitope", level="residue", pdb="my_pdb_path") # Return heatmap projected onto PDB

graphics <- visualise_hdx_data(results, type="protection", level="peptide") # Return heatmap for peptide residue-number ranges
graphics <- visualise_hdx_data(results, type="protection", level="residue", fasta = "my_fasta_path") # Return heatmap
graphics <- visualise_hdx_data(results, type="protection", level="residue", pdb="my_pdb_path") # Return heatmap projected onto PDB

# GUI: Make GUI by assembling these building blocks
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcwoKYGBge3J9CmxpYnJhcnkoImhkeHN0YXRzIikKbGlicmFyeSgiZHBseXIiKQpsaWJyYXJ5KCJnZ3Bsb3QyIikKbGlicmFyeSgiUkNvbG9yQnJld2VyIikKbGlicmFyeSgidGlkeXIiKQpsaWJyYXJ5KCJwaGVhdG1hcCIpCmxpYnJhcnkoInNjYWxlcyIpCmxpYnJhcnkoInZpcmlkaXMiKQpsaWJyYXJ5KCJwYXRjaHdvcmsiKQpsaWJyYXJ5KCJCaW9zdHJpbmdzIikKbGlicmFyeSgieGZ1biIpCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmBgYAoKYGBge3J9CnNvdXJjZSgiUi90ZXN0X3NjcmlwdF9hcHAyLlIiKQpgYGAKCgojIFBhcnNpbmcgUmF3IERhdGEKClBhcnNlIGlucHV0IHJhdyBkYXRhIGFuZCBvdXRwdXQgYFFGZWF0dXJlc2Agb2JqZWN0IGluc3RhbmNlIGdpdmVuIGEgQ1NWIGZpbGUgcGF0aCBmb3IgZGlmZmVyZW50IHRlc3QgY2FzZXMuCgpgYGB7cn0KIyBGaXJzdCB0ZXN0IGNhc2UKY3N2X2ZpbGVwYXRoIDwtICIvaG9tZXMvc2FuanVhbi9SL3g4Nl82NC1wYy1saW51eC1nbnUtbGlicmFyeS80LjEvaGR4c3RhdHMvZXh0ZGF0YS9NQlAuY3N2IgoKIyBDQVNFIDE6IFBhcnNlIGRhdGEsIGdpdmVuIGlucHV0IGRhdGEgZmlsZSBwYXRoICsgcGFyYW1ldGVyIGZpbGUKaGR4X2RhdGEgPC0gZXh0cmFjdF9oZHhfZGF0YShjc3ZfZmlsZXBhdGgsIHBhcmFtZXRlcl9maWxlID0gInZpZ25ldHRlcy9kYXRhL215cGFyYW1ldGVycy5oZHhwIikKCiMgQ0FTRSAyOiBQYXJzZSBkYXRhLCBnaXZlbiBpbnB1dCBkYXRhIGZpbGUgcGF0aCArIGxpc3Qgb2YgcGFyYW1ldGVycwpkYXRhIDwtIHJlYWRfY3N2KGNzdl9maWxlcGF0aCkKbXlwYXJhbWV0ZXJzIDwtIG1ha2VfcGFyYW1ldGVyX2ZpbGUoZGF0YSkKaGR4X2RhdGEgPC0gZXh0cmFjdF9oZHhfZGF0YShjc3ZfZmlsZXBhdGgsIHBhcmFtZXRlcnMgPSBteXBhcmFtZXRlcnMpCgojIENBU0UgMzogUGFyc2UgZGF0YSwgZ2l2ZW4gaW5wdXQgZGF0YSBmaWxlIHBhdGguIFdvcmsgb3V0IHBhcmFtZXRlcnMgZm9yIHByZS1wcm9jZXNzaW5nIHVzaW5nIGludGVyYWN0aXZlIG1vZGUKaGR4X2RhdGEgPC0gZXh0cmFjdF9oZHhfZGF0YShjc3ZfZmlsZXBhdGgsIHBhcmFtZXRlcl9maWxlID0gInZpZ25ldHRlcy9kYXRhL215cGFyYW1ldGVycy5oZHhwIiwgaW50ZXJhY3RpdmUgPSBUUlVFKQpgYGAKCiMgRGF0YSBBbmFseXNpcyBvZiBEZXV0ZXJpdW0gVXB0YWtlIEtpbmV0aWNzCgojIyBURVNUIDE6CgojIyMgUGVyZm9ybSBEYXRhIEFuYWx5c2lzOiBQbGFpbiBmaXR0aW5nCgoqIERldXRlcml1bS11cGRhdGUgZml0dGluZyBmb3IgYWxsIHBlcHRpZGVzCiogUGxhaW4gZml0dGluZyAtIG5vdCBjb21wYXJpbmcgYWdhaW5zdCBhIHJlZmVyZW5jZSBzdGF0ZSBvciBjb25kaXRpb24KCmBgYHtyfQojIElOUFVUCmRhdGFfc2VsZWN0aW9uIDwtIGhkeF9kYXRhWywxOjI0XQphbGxfcGVwdGlkZXMgPC0gcm93bmFtZXMoZGF0YV9zZWxlY3Rpb24pW1sxXV0Kc3RhcnRpbmdfcGFyYW1ldGVycyA8LSBsaXN0KGEgPSBOVUxMLCBiID0gMC4wMDEsICBkID0gTlVMTCwgcCA9IDEpCiMgT1VUUFVUCnJlc3VsdHMgPC0gYW5hbHlzZV9raW5ldGljcyhkYXRhID0gZGF0YV9zZWxlY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImZpdCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVwdGlkZV9zZWxlY3Rpb24gPSBhbGxfcGVwdGlkZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSBzdGFydGluZ19wYXJhbWV0ZXJzKQpgYGAKCmBgYHtyfQpyZXN1bHRzJG1ldGhvZApgYGAKCiMjIyBWaXN1YWxpc2UgT3V0cHV0IGZyb20gRGF0YSBBbmFseXNpcwoKVmlldyBmaXR0aW5nIGN1cnZlcyBvZiBEZXUtdXB0YWtlIGtpbmV0aWNzIGZvciBhbGwgYXZhaWxhYmxlIGNvbmRpdGlvbnMgYXNzb2NpYXRlZCB0byBzZWxlY3RlZCBwZXB0aWRlcwoKYGBge3IsIGZpZy53aWR0aD0gMjIsIGZpZy5oZWlnaHQgPSA3fQpncmFwaGljc19raW5ldGljIDwtIHZpc3VhbGlzZV9oZHhfZGF0YShyZXN1bHRzLCB0eXBlPSJraW5ldGljcyIpIApncmFwaGljc19raW5ldGljW1sxXV0gfCBncmFwaGljc19raW5ldGljW1syXV0gfCBncmFwaGljc19raW5ldGljW1szXV0KYGBgCgpWaWV3IGZvcmVzdCBwbG90cyBzaG93aW5nIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdXB0YWtlIG1lYXN1cmVtZW50cyBiZXR3ZWVuIGNvbmRpdGlvbnMsIHBsdXMgdGhlIGRpc3BlcnNpb24gYmV0d2VlbiB0aGVpciByZXNwZWN0aXZlIGZpdHRpbmcgbW9kZWwgcGFyYW1ldGVycwoKYGBge3IsIGZpZy53aWR0aD0gMjIsIGZpZy5oZWlnaHQgPSAxMH0KZ3JhcGhpY3MgPC0gdmlzdWFsaXNlX2hkeF9kYXRhKHJlc3VsdHMsIHR5cGU9ImZvcmVzdCIpCmBgYApDb21iaW5lIGdyYXBoaWNhbCBvdXRwdXRzIGluIGEgc2luZ2xlIGNhbnZhcwoKYGBge3IsIGZpZy53aWR0aD0gMjIsIGZpZy5oZWlnaHQgPSAxMH0KZ3JhcGhpY3NbWzFdXSB8IGdyYXBoaWNzX2tpbmV0aWNbWzFdXQpgYGAKRGlzcGxheSBudW1lcmljYWwgdmFsdWVzIGFzIGEgdGFibGUgCgpgYGB7cn0KZ3JhcGhpY3NbWzFdXSRkYXRhCmBgYAoKIyMjIFBlcmZvcm0gRGF0YSBBbmFseXNpczogRGlmZmVyZW50aWFsIGZpdHRpbmcKCiogRGV1dGVyaXVtLXVwZGF0ZSBmaXR0aW5nIGZvciBkYXRhIHNlbGVjdGlvbiAKKiBEaWZmZXJlbnRpYWwgZml0dGluZyAtIHdpdGggcmVzcGVjdCB0byBhICpzaW5nbGUgcmVmZXJlbmNlKiBwZXB0aWRlCgpgYGB7cn0KIyBJTlBVVCAKZGF0YV9zZWxlY3Rpb24gPC0gaGR4X2RhdGFbLDE6MTAwXQphbGxfcGVwdGlkZXMgPC0gcm93bmFtZXMoZGF0YV9zZWxlY3Rpb24pW1sxXV0gIyBnZXQgYWxsIHBlcHRpZGVzCnN0YXJ0aW5nX3BhcmFtZXRlcnMgPC0gbGlzdChhID0gTlVMTCwgYiA9IDAuMDAwMSwgIGQgPSBOVUxMLCBwID0gMSkKCiMgT1VUUFVUCnJlc3VsdHMgPC0gYW5hbHlzZV9raW5ldGljcyhkYXRhID0gZGF0YV9zZWxlY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImRmaXQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBlcHRpZGVfc2VsZWN0aW9uID0gYWxsX3BlcHRpZGVzWzM3XSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydCA9IHN0YXJ0aW5nX3BhcmFtZXRlcnMpCgpgYGAKCmBgYHtyfQpncmFwaGljcyA8LSB2aXN1YWxpc2VfaGR4X2RhdGEocmVzdWx0cywgdHlwZT0ia2luZXRpY3MiKQpgYGAKClZpZXcgZ3JhcGhpY2FsIG91dHB1dCBmb3IgcmVmZXJlbmNlIHBlcHRpZGUgc2hvd24gaW4gTEhTIFRvcCBjb3JuZXIuIAoKYGBge3J9CmdyYXBoaWNzCmBgYAoKIyMgVEVTVCBDQVNFIDIKClNpbmdsZS1kb21haW4gYW50aWJvZHkgKHNkQWIpIGJpbmRpbmcgYXNzYXlzIHRvIEhPSVAuCgoKYGBge3J9CmNzdl9maWxlcGF0aCA8LSAiL2hvbWVzL3Nhbmp1YW4vUi94ODZfNjQtcGMtbGludXgtZ251LWxpYnJhcnkvNC4xL2hkeHN0YXRzL2V4dGRhdGEvTjY0MTg0XzFhMl9zdGF0ZS5jc3YiIApgYGAKCgpgYGB7cn0KZGF0YSA8LSByZWFkX2Nzdihjc3ZfZmlsZXBhdGgpCm1ha2VfcGFyYW1ldGVyX2ZpbGUoZGF0YSwgc2F2ZSA9ICJ2aWduZXR0ZXMvZGF0YS9ONjQxODRfMWEyX3N0YXRlLmhkeHAiKQpgYGAKCgpgYGB7cn0KaGR4X2RhdGEgPC0gZXh0cmFjdF9oZHhfZGF0YShjc3ZfZmlsZXBhdGgsIHBhcmFtZXRlcl9maWxlID0gInZpZ25ldHRlcy9kYXRhL042NDE4NF8xYTJfc3RhdGUuaGR4cCIpCmBgYAoKCmBgYHtyfQoKIyBJTlBVVCAKZGF0YV9zZWxlY3Rpb24gPC0gaGR4X2RhdGFbLDE6MzNdCmFsbF9wZXB0aWRlcyA8LSByb3duYW1lcyhkYXRhX3NlbGVjdGlvbilbWzFdXSAjIGdldCBhbGwgcGVwdGlkZXMKc3RhcnRpbmdfcGFyYW1ldGVycyA8LSBsaXN0KGEgPSBOVUxMLCBiID0gMC4wMSwgIGQgPSBOVUxMKQpmb3JtdWxhID0gdmFsdWUgfiBhICogKDEgLSBleHAoLWIqKHRpbWVwb2ludCkpKSArIGQKCiMgT1VUUFVUCnJlc3VsdHMgPC0gYW5hbHlzZV9raW5ldGljcyhkYXRhID0gZGF0YV9zZWxlY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImRmaXQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBlcHRpZGVfc2VsZWN0aW9uID0gYWxsX3BlcHRpZGVzWzNdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gc3RhcnRpbmdfcGFyYW1ldGVycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm11bGEgPSBmb3JtdWxhKQpgYGAKCgpgYGB7cn0KZ3JhcGhpY3MgPC0gdmlzdWFsaXNlX2hkeF9kYXRhKHJlc3VsdHMsIHR5cGU9ImtpbmV0aWNzIikKCmN1c3RvbV9jb2xvcnMgPC0gc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg4LCBuYW1lID0gIlNldDIiKSkoMTEpKQpncmFwaGljcyArIGN1c3RvbV9jb2xvcnMKYGBgCgoKIyBURVNUIENBU0UgMwoKT3JpZ2luYWwgcmF3IGRhdGEKYGBge3J9CiNjc3ZfZmlsZXBhdGggPC0gImluc3QvZXh0ZGF0YS9Qcm9qZWN0XzJfU2VjQV9DbHVzdGVyX0RhdGEuY3N2IgojZGF0YSA8LSByZWFkX2Nzdihjc3ZfZmlsZXBhdGgpCiNkYXRhJFJlcGxpY2F0ZSA8LSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KGRhdGEkRmlsZSwgc3BsaXQ9Il8iKSwgZnVuY3Rpb24oeCkgdGFpbCh4LCBuPTEpKSkKI3dyaXRlX2NzdihkYXRhLCBmaWxlID0gImluc3QvZXh0ZGF0YS9Qcm9qZWN0XzJfU2VjQV9DbHVzdGVyX0RhdGFfZWRpdGVkLmNzdiIpCgpjc3ZfZmlsZXBhdGggPC0gImluc3QvZXh0ZGF0YS9Qcm9qZWN0XzJfU2VjQV9DbHVzdGVyX0RhdGFfZWRpdGVkLmNzdiIKYGBgCgpIYWQgdG8gcHJlLXByb2Nlc3MgaXRzIGNvbnRlbnQgdG8gYWRkIFJlcGxpY2F0ZSBjb2x1bW4KCmBgYHtyfQpkYXRhIDwtIHJlYWRfY3N2KGNzdl9maWxlcGF0aCkKbWFrZV9wYXJhbWV0ZXJfZmlsZShkYXRhLCBzYXZlID0gInZpZ25ldHRlcy9kYXRhL1Byb2plY3RfMl9TZWNBX0NsdXN0ZXJfRGF0YS5oZHhwIikKYGBgCgpgYGB7cn0KaGR4X2RhdGEgPC0gZXh0cmFjdF9oZHhfZGF0YShjc3ZfZmlsZXBhdGgsIHBhcmFtZXRlcl9maWxlID0gInZpZ25ldHRlcy9kYXRhL1Byb2plY3RfMl9TZWNBX0NsdXN0ZXJfRGF0YS5oZHhwIikKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQgPSAyMCwgZmlnLndpZHRoID0gODAsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwaGVhdG1hcCh0KGFzc2F5KGhkeF9kYXRhKSksCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCAKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gRkFMU0UsCiAgICAgICAgIGNvbG9yID0gYnJld2VyLnBhbChuID0gOSwgbmFtZSA9ICJCdVB1IiksCiAgICAgICAgIG1haW4gPSAic2VjQSBoZWF0bWFwIiwgCiAgICAgICAgIGZvbnRzaXplID0gMTQsCiAgICAgICAgIGxlZ2VuZF9icmVha3MgPSBjKDAsIDEsIDIsIDMsIDQsIDUsIDYsIG1heChhc3NheShoZHhfZGF0YSkpKSwKICAgICAgICAgbGVnZW5kX2xhYmVscyA9IGMoIjAiLCAiMSIsICIyIiwgIjMiLCAiNCIsICI1IiwgIjYiLCAiSW5jb3Jwb3JhdGlvbiIpKQpgYGAKCgpgYGB7cn0KZ3JhcGhpY3MgPC0gdmlzdWFsaXNlX2hkeF9kYXRhKHJlc3VsdHMsIHR5cGU9ImtpbmV0aWNzIikgIyBSRUFEWQpncmFwaGljcyA8LSB2aXN1YWxpc2VfaGR4X2RhdGEocmVzdWx0cywgdHlwZT0iZm9yZXN0IikgIyBSRUFEWQoKIyBOT1RFOiBUaGlzIG9ubHkgd29ya3MgdG8gY29tcGFyZSBPTkxZIFRXTyBkaXN0aW5jdCBjb25kaXRpb25zLCBmb3IgdGhlIHNhbWUgdGltZXBvaW50LCBmb3IgYWxsIHBlcHRpZGUgZnJhZ21lbnRzCiMgVGhpcyBuZWVkcyB0byBkZWZpbmUgYSBCQVNFTElORSBzdGF0ZS9jb25kaXRpb24gYW5kIGFuIEFMVEVSTkFURSBzYXRlL2NvbmRpdGlvbgojIE5PVEU6IERhdGEgbXVzdCBiZSBub3JtYWxpc2VkIGJ5IGludGVyY2VwdCAKZ3JhcGhpY3MgPC0gdmlzdWFsaXNlX2hkeF9kYXRhKHJlc3VsdHMsIHR5cGU9Im1hbmhhdHRlbiIsIHJlZmVyZW5jZSA9IE5VTEwpIyA8PDw8LS0tIE5FWFQKCiMgTk9URTogTk9UIFNVUkUgPz8KZ3JhcGhpY3MgPC0gdmlzdWFsaXNlX2hkeF9kYXRhKHJlc3VsdHMsIHR5cGU9ImVwaXRvcGUiLCBsZXZlbD0icGVwdGlkZSIsIGZhc3RhID0gIm15X2Zhc3RhX3BhdGgiKSAjIFJldHVybiBhbiBFcGl0b3BlTWFwCmdyYXBoaWNzIDwtIHZpc3VhbGlzZV9oZHhfZGF0YShyZXN1bHRzLCB0eXBlPSJlcGl0b3BlIiwgbGV2ZWw9InJlc2lkdWUiLCBmYXN0YSA9ICJteV9mYXN0YV9wYXRoIikgIyBSZXR1cm4gaGVhdG1hcApncmFwaGljcyA8LSB2aXN1YWxpc2VfaGR4X2RhdGEocmVzdWx0cywgdHlwZT0iZXBpdG9wZSIsIGxldmVsPSJyZXNpZHVlIiwgcGRiPSJteV9wZGJfcGF0aCIpICMgUmV0dXJuIGhlYXRtYXAgcHJvamVjdGVkIG9udG8gUERCCgpncmFwaGljcyA8LSB2aXN1YWxpc2VfaGR4X2RhdGEocmVzdWx0cywgdHlwZT0icHJvdGVjdGlvbiIsIGxldmVsPSJwZXB0aWRlIikgIyBSZXR1cm4gaGVhdG1hcCBmb3IgcGVwdGlkZSByZXNpZHVlLW51bWJlciByYW5nZXMKZ3JhcGhpY3MgPC0gdmlzdWFsaXNlX2hkeF9kYXRhKHJlc3VsdHMsIHR5cGU9InByb3RlY3Rpb24iLCBsZXZlbD0icmVzaWR1ZSIsIGZhc3RhID0gIm15X2Zhc3RhX3BhdGgiKSAjIFJldHVybiBoZWF0bWFwCmdyYXBoaWNzIDwtIHZpc3VhbGlzZV9oZHhfZGF0YShyZXN1bHRzLCB0eXBlPSJwcm90ZWN0aW9uIiwgbGV2ZWw9InJlc2lkdWUiLCBwZGI9Im15X3BkYl9wYXRoIikgIyBSZXR1cm4gaGVhdG1hcCBwcm9qZWN0ZWQgb250byBQREIKCiMgR1VJOiBNYWtlIEdVSSBieSBhc3NlbWJsaW5nIHRoZXNlIGJ1aWxkaW5nIGJsb2NrcwoKYGBgCgo=